package cn.tedu.charging.order.rabbitmq;

import cn.tedu.charging.order.config.RabbitMQConfig;
import cn.tedu.charging.order.constant.OrderStatusConstant;
import cn.tedu.charging.order.dao.mapper.OrderSuccessMapper;
import cn.tedu.charging.order.dao.repository.OrderRepository;
import cn.tedu.charging.order.pojo.po.ChargingBillSuccessPO;
import cn.tedu.charging.order.pojo.po.OrderMQPO;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * rabbit-mq消费者
 * 消费的是死信队列
 */
@Slf4j
@Component
public class RabbitMQOrderConsumer {

    //8.5 数据操作层和业务层耦合了  RabbitMQOrderConsumer 依赖了 baomidou的 UpdateWrapper
    //解耦 如何解? 中间加 数据操作层dao
    //@Autowired
    //private OrderSuccessMapper orderSuccessMapper;
    //与数据层都通过Repository对话，不再直接找mapper对话
    @Autowired
    private OrderRepository orderRepository;

    /**
     * @RabbitListener 类似于 test中写的回调类 RabbitMQTests#subscribe DeliverCallback
     * 只需给 @RabbitListener 注解 加上我们要监听的队列,死信队列
     * 当死信队列里有消息的时候,会调用consumeOrderDeadLetter
     * @param orderMQPO
     * @param message
     * @param channel
     */
    //记得把RabbitMQConfig的这条属性改为public
    @RabbitListener(queues = RabbitMQConfig.ORDER_DEAD_LETTER_QUEUE)    //加注解，监听死信队列
    public void consumeOrderDeadLetter(OrderMQPO orderMQPO, Message message, Channel channel) throws IOException {
        //这里的Message、Channel导包别弄错了      为啥非要用指定的导包？？
        //@RabbitListener和Message都是spring的amqp的，所以这个回调类算是用spring方法建的，代码量少，只有Channel是rabbit的
        //这个方法的作用相当于是test里被调用的回调方法
        log.debug("消费死信队列消息:{}",orderMQPO);


        //把正在充电中的订单数据的状态修改为 异常结束
        //update charging_bill_success set bill_status = 3 where bill_id = orderNo and bill_status = 1
        //8.5 解耦，不再直接访问mapper   里面的2是程序员经常偷懒替代 To 。。。
        orderRepository.updateOrderStatusFromProcess2ExceptionEnd(orderMQPO.getOrderNo());



        //手动ack     之前yaml中配置为手动ack，这里就是手动给MQ发送ack，告诉MQ，消费消息成功，MQ可以删除死信队列消息了
        // 至于具体代码，没看懂，不过往下查看源码，就是long类型
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        channel.basicAck(deliveryTag,true); //又特么把异常在类上抛了
        /* 如何保证消息不丢失?
         生产者 发的时候不丢 可以通过confirm机制 , 确认rabbit-mq 已经收到生产者发送的消息
         rabbit-mq 本身不丢 queue和exchange都需要开启持久化
         消息者 消费消息不丢 手动ack

         消息队列中的ack是什么意思?
         消息者拿到消息，处理业务，更新订单状态
         正常情况：更新订单状态成功 返回ack给消息队列
         消息队列收到ack后,认为消费者消费成功,会把这个条消息从消息队列删除,消费者继续消费下一条消息
         异常情况：数据库出现异常,更新失败,不返回ack给消息队列
         消息队列没有收到ack,认为消费者消费失败,不会删除这条消息,会一直保存,
         等数据库修好了,继续重复消费这条消息,数据没丢,继续更新订单的状态 */
    }


}
